/**
 * @author alteredq / http://alteredqualia.com/
 */

import {
	Clock,
	LinearFilter,
	Mesh,
	OrthographicCamera,
	PlaneBufferGeometry,
	RGBAFormat,
	Vector2,
	WebGLRenderTarget
} from "./three.module";
import { CopyShader } from "./CopyShader";
import { ShaderPass } from "./ShaderPass";
import { MaskPass } from "./MaskPass";
import { ClearMaskPass } from "./MaskPass";

var EffectComposer = function (renderer, renderTarget) {

	this.renderer = renderer;

	if (renderTarget === undefined) {

		var parameters = {
			minFilter: LinearFilter,
			magFilter: LinearFilter,
			format: RGBAFormat,
			stencilBuffer: false
		};

		var size = renderer.getSize(new Vector2());
		this._pixelRatio = renderer.getPixelRatio();
		this._width = size.width;
		this._height = size.height;

		renderTarget = new WebGLRenderTarget(this._width * this._pixelRatio, this._height * this._pixelRatio, parameters);
		renderTarget.texture.name = 'EffectComposer.rt1';

	} else {

		this._pixelRatio = 1;
		this._width = renderTarget.width;
		this._height = renderTarget.height;

	}

	this.renderTarget1 = renderTarget;
	this.renderTarget2 = renderTarget.clone();
	this.renderTarget2.texture.name = 'EffectComposer.rt2';

	this.writeBuffer = this.renderTarget1;
	this.readBuffer = this.renderTarget2;

	this.renderToScreen = true;

	this.passes = [];

	// dependencies

	if (CopyShader === undefined) {

		console.error('THREE.EffectComposer relies on CopyShader');

	}

	if (ShaderPass === undefined) {

		console.error('THREE.EffectComposer relies on ShaderPass');

	}

	this.copyPass = new ShaderPass(CopyShader);

	this.clock = new Clock();

};

Object.assign(EffectComposer.prototype, {

	swapBuffers: function () {

		var tmp = this.readBuffer;
		this.readBuffer = this.writeBuffer;
		this.writeBuffer = tmp;

	},

	addPass: function (pass) {

		this.passes.push(pass);
		pass.setSize(this._width * this._pixelRatio, this._height * this._pixelRatio);

	},

	insertPass: function (pass, index) {

		this.passes.splice(index, 0, pass);
		pass.setSize(this._width * this._pixelRatio, this._height * this._pixelRatio);

	},

	isLastEnabledPass: function (passIndex) {

		for (var i = passIndex + 1; i < this.passes.length; i++) {

			if (this.passes[i].enabled) {

				return false;

			}

		}

		return true;

	},

	render: function (deltaTime) {

		// deltaTime value is in seconds

		if (deltaTime === undefined) {

			deltaTime = this.clock.getDelta();

		}

		var currentRenderTarget = this.renderer.getRenderTarget();

		var maskActive = false;

		var pass, i, il = this.passes.length;

		for (i = 0; i < il; i++) {

			pass = this.passes[i];

			if (pass.enabled === false) continue;

			pass.renderToScreen = (this.renderToScreen && this.isLastEnabledPass(i));
			pass.render(this.renderer, this.writeBuffer, this.readBuffer, deltaTime, maskActive);

			if (pass.needsSwap) {

				if (maskActive) {

					var context = this.renderer.getContext();
					var stencil = this.renderer.state.buffers.stencil;

					//context.stencilFunc( context.NOTEQUAL, 1, 0xffffffff );
					stencil.setFunc(context.NOTEQUAL, 1, 0xffffffff);

					this.copyPass.render(this.renderer, this.writeBuffer, this.readBuffer, deltaTime);

					//context.stencilFunc( context.EQUAL, 1, 0xffffffff );
					stencil.setFunc(context.EQUAL, 1, 0xffffffff);

				}

				this.swapBuffers();

			}

			if (MaskPass !== undefined) {

				if (pass instanceof MaskPass) {

					maskActive = true;

				} else if (pass instanceof ClearMaskPass) {

					maskActive = false;

				}

			}

		}

		this.renderer.setRenderTarget(currentRenderTarget);

	},

	reset: function (renderTarget) {

		if (renderTarget === undefined) {

			var size = this.renderer.getSize(new Vector2());
			this._pixelRatio = this.renderer.getPixelRatio();
			this._width = size.width;
			this._height = size.height;

			renderTarget = this.renderTarget1.clone();
			renderTarget.setSize(this._width * this._pixelRatio, this._height * this._pixelRatio);

		}

		this.renderTarget1.dispose();
		this.renderTarget2.dispose();
		this.renderTarget1 = renderTarget;
		this.renderTarget2 = renderTarget.clone();

		this.writeBuffer = this.renderTarget1;
		this.readBuffer = this.renderTarget2;

	},

	setSize: function (width, height) {

		this._width = width;
		this._height = height;

		var effectiveWidth = this._width * this._pixelRatio;
		var effectiveHeight = this._height * this._pixelRatio;

		this.renderTarget1.setSize(effectiveWidth, effectiveHeight);
		this.renderTarget2.setSize(effectiveWidth, effectiveHeight);

		for (var i = 0; i < this.passes.length; i++) {

			this.passes[i].setSize(effectiveWidth, effectiveHeight);

		}

	},

	setPixelRatio: function (pixelRatio) {

		this._pixelRatio = pixelRatio;

		this.setSize(this._width, this._height);

	}

});


var Pass = function () {

	// if set to true, the pass is processed by the composer
	this.enabled = true;

	// if set to true, the pass indicates to swap read and write buffer after rendering
	this.needsSwap = true;

	// if set to true, the pass clears its buffer before rendering
	this.clear = false;

	// if set to true, the result of the pass is rendered to screen. This is set automatically by EffectComposer.
	this.renderToScreen = false;

};

Object.assign(Pass.prototype, {

	setSize: function ( /* width, height */) { },

	render: function ( /* renderer, writeBuffer, readBuffer, deltaTime, maskActive */) {

		console.error('THREE.Pass: .render() must be implemented in derived pass.');

	}

});

// Helper for passes that need to fill the viewport with a single quad.
Pass.FullScreenQuad = (function () {

	var camera = new OrthographicCamera(- 1, 1, 1, - 1, 0, 1);
	var geometry = new PlaneBufferGeometry(2, 2);

	var FullScreenQuad = function (material) {

		this._mesh = new Mesh(geometry, material);

	};

	Object.defineProperty(FullScreenQuad.prototype, 'material', {

		get: function () {

			return this._mesh.material;

		},

		set: function (value) {

			this._mesh.material = value;

		}

	});

	Object.assign(FullScreenQuad.prototype, {

		dispose: function () {

			this._mesh.geometry.dispose();

		},

		render: function (renderer) {

			renderer.render(this._mesh, camera);

		}

	});

	return FullScreenQuad;

})();

export { EffectComposer, Pass };
